/*
 * Copyright [2022] [liufeng]
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.f.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.ChainWrappers;
import com.f.base.PageRequest;
import com.f.bo.IdReasonBo;
import com.f.bo.TaskDto;
import com.f.bo.flow.FlowStartProcessBo;
import com.f.client.sys.SysRoleClient;
import com.f.constant.Constant;
import com.f.entity.FlowDesign;
import com.f.mapper.FlowDesignMapper;
import com.f.service.FlowService;
import lombok.RequiredArgsConstructor;
import org.flowable.engine.ProcessEngine;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 工作流 服务实现类
 * </p>
 *
 * @author liuf
 * @date 2022-08-04
 */
@Service
@RequiredArgsConstructor
public class FlowServiceImpl implements FlowService {

    private static final Logger LOGGER = LoggerFactory.getLogger(FlowServiceImpl.class);
    private static final String BPMN_20_XML = ".bpmn20.xml";

    private final ProcessEngine processEngine;

    private final FlowDesignMapper flowDesignMapper;

    private final SysRoleClient sysRoleClient;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public String deployment(Long id) {
        LOGGER.info("deployment:{}", id);
        FlowDesign flowDesign = flowDesignMapper.selectById(id);
        Deployment deploy = processEngine.getRepositoryService().createDeployment()
                .name(flowDesign.getName())
                .key(flowDesign.getFlowId())
                .addString(flowDesign.getFlowId() + BPMN_20_XML, flowDesign.getXml())
                .deploy();
        LOGGER.info("deploy success:{}", deploy);
        // 修改为已部署
        ChainWrappers.lambdaUpdateChain(flowDesignMapper)
                .eq(FlowDesign::getId, flowDesign.getId())
                .set(FlowDesign::getIsDeploy, Constant.YES)
                .update();
        return deploy.getId();
    }

    @Override
    public String selectDefinition(String deploymentId) {
        LOGGER.info("selectDefinition:{}", deploymentId);
        ProcessDefinition singleResult = processEngine.getRepositoryService()
                .createProcessDefinitionQuery()
                .deploymentId(deploymentId)
                .singleResult();
        if (singleResult == null) {
            return null;
        }
        Map<String, Object> map = new HashMap<>();
        map.put("id", singleResult.getId());
        map.put("category", singleResult.getCategory());
        map.put("name", singleResult.getName());
        map.put("key", singleResult.getKey());
        map.put("description", singleResult.getDescription());
        map.put("version", singleResult.getVersion());
        map.put("resourceName", singleResult.getResourceName());
        map.put("deploymentId", singleResult.getDeploymentId());
        map.put("diagramResourceName", singleResult.getDiagramResourceName());
        map.put("isSuspended", singleResult.isSuspended());
        map.put("tenantId", singleResult.getTenantId());
        map.put("derivedFrom", singleResult.getDerivedFrom());
        map.put("derivedFromRoot", singleResult.getDerivedFromRoot());
        map.put("derivedVersion", singleResult.getDerivedVersion());
        map.put("engineVersion", singleResult.getEngineVersion());
        LOGGER.info("selectDefinition singleResult:{}", map);
        return map.toString();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public String startProcess(FlowStartProcessBo startProcessBo) {
        LOGGER.info("startProcess:{}", startProcessBo);
        ProcessInstance processInstance = processEngine.getRuntimeService()
                .startProcessInstanceByKey(startProcessBo.getKey(), startProcessBo.getBusinessKey(), startProcessBo.getVariables());
        LOGGER.info("processInstance:{}", processInstance.getProcessDefinitionId());
        return processInstance.getId();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteProcess(IdReasonBo idReasonBo) {
        processEngine.getRuntimeService().deleteProcessInstance(idReasonBo.getId(), idReasonBo.getReason());
    }

    @Override
    public Page<TaskDto> queryTask(Long userId, PageRequest<Boolean> page) {
        LOGGER.info("queryTask:{}", userId);
        TaskQuery taskQuery = processEngine.getTaskService().createTaskQuery()
                .or()
                .taskCandidateOrAssigned(userId.toString());
        Set<Long> roleSet = sysRoleClient.getByUserId(userId);
        if (!roleSet.isEmpty()) {
            taskQuery.taskCandidateGroupIn(roleSet.stream().map(String::valueOf)
                    .collect(Collectors.toList()));
        }
        taskQuery.endOr().orderByTaskCreateTime().desc();
        long count = taskQuery.count();
        List<Task> taskList = taskQuery.listPage(page.getStart(), page.getSize());
        if (CollectionUtils.isEmpty(taskList)) {
            return null;
        }
        Page<TaskDto> dtoPage = new Page<>(page.getPage(), page.getSize(), count);
        dtoPage.setRecords(taskList.stream().map(TaskDto::byTaskInfo).collect(Collectors.toList()));
        return dtoPage;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void complete(String status, String taskId, Map<String, Object> variables) {
        LOGGER.info("complete:{}", status);
        if (variables == null) {
            variables = new HashMap<>(8);
        }
        variables.put(Constant.FLOW_STATUS, status);
        processEngine.getTaskService().complete(taskId, variables);
    }
}
